﻿// Author - Owen Emlen (owene_1998@yahoo.com)
using System.Runtime.Serialization;
using System;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Threading;
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC.ThreadSafeObjects
{
#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    [Serializable]
    [DataContract]
    public class CachedByKey<TKey, T> : GenericCacheAbstractBase<T>, ICacheBase<T> where T : class, IKeyedOnProperty<TKey>
    {
        public CachedByKey() { }
        public CachedByKey(TPopulateCacheDelegate<T> populateCacheDelegate) { SetPopulateCacheDelegate(populateCacheDelegate); }

        [NonSerialized, XmlIgnore]
        protected ThreadSafeLookupOnAutoKey<TKey, T> _item;

        public override bool IsCacheValid() { return (_item != null && !_dirty); }
        public override bool IsCacheValid(object populateCacheParameter) { return (!_dirty && _item != null && _item[(TKey)populateCacheParameter] != null); }

        public virtual T GetItem(TKey populateCacheParameter, params object[] args)
        {
            return GetItem(populateCacheParameter, args);
        }

        public override T GetItem(object populateCacheParameter, params object[] args)
        {
            T item = null;

            if (IsCacheValid(populateCacheParameter))
            {
                item = _item[(TKey)populateCacheParameter];
            }

            if (item == null)
            {
                if (_populateCacheDelegate != null)
                {
                    if (_repopLock == null)
                    {
                        Interlocked.CompareExchange<Lockable>(ref _repopLock, new Lockable(), null);
                    }

                    while (_repopLock.Locked) { Thread.Sleep(10); }

                    _repopLock.AquireLock();
                    {
                        try
                        {
                            if (_dirty)
                            {
                                Interlocked.Exchange<ThreadSafeLookupOnAutoKey<TKey, T>>(ref _item, null);
                                _dirty = false;
                            }

                            if (!IsCacheValid((TKey)populateCacheParameter))
                            {
                                T outval;

                                if (_populateCacheDelegate(populateCacheParameter, out outval, args))
                                {
                                    if (_item == null)
                                    {
                                        Interlocked.Exchange<ThreadSafeLookupOnAutoKey<TKey, T>>(ref _item, new ThreadSafeLookupOnAutoKey<TKey, T>());
                                    }

                                    _item.AddOrSet((TKey)populateCacheParameter, outval);
                                    return outval;
                                }
                            }
                        }
                        finally
                        {
                            _repopLock.ReleaseLock();
                        }
                    }
                }
            }
            return item;
        }

        public override T GetItem()
        {
            return GetItem(_defaultPopulateParam, _storedArgs);
        }
    }
}
